# -*- coding: utf-8 -*-
"""
计算股票的移动平均和指数平均收盘价格.

1.获取最后一天交易的日期
2.取出最后一天有交易的股票代码，也就是没有停牌或退市
3.对每只股票取出近242天的交易数据，计算ema,coe等
4.根据计算出来的ema，coe判断八线理论中持股线高或低于生命线的情况
5.将数据入库，同时备份一份到本地文件中
"""
import pandas as pd
import time
import os

from stock_py.config import config as conf
from stock_py.calc_indicator import my_sklearn as sk
from stock_py.tools.database import stock_db as mydb
from stock_py.tools.time_py import get_time as mytime

# 全局参数
# 计算结果存放目录
calc_result_path = conf.g_var.calc_result_path
calc_result_path2 = conf.g_var.calc_result_path2

#-----------------------------------------
def take_all_data(conn, dates, limit_row=-242):
    '''将stock全部需要的数据取出来，而不是一次只取一只股票的数据。
    减少查询次数，因为大部分时间都花在和数据库的交互上面了.
    dates:读取指定日期时间点的数据。
    limit_row：计算ema至少需要的数据量,235+7=242
    '''
    start_time = mytime.delta_days(dates,limit_row)  # 取数开始时间
    stop_time = dates  # 取数结束时间，即取数时间点
    # --要求在指定日期改股票没有停牌，退市什么的
    sql = r"""select a.code,a.name, a.dates,
       a.open,a.high,a.low,a.close,a.qfq, a.volume 
       from ( select * from stock_qx where dates <= 'stop_time'
               and dates >= 'start_time'
               and code in (select code from stock_qx 
                             where dates='stop_time' )  
        ) a order by a.code, dates asc """
    sql = sql.replace('start_time',start_time)
    sql = sql.replace('stop_time',stop_time)
    data = pd.read_sql(sql, conn)
    data.columns = [col.lower() for col in data.columns]  # 列名转小写
    data.index = data['code']  # 将股票代码作为索引
    return data
    
def stock_ema(code, all_data, ma_list=[7,15,25,34,56,235],vo=120):
    '''计算股票未复权收盘价的ema数据.
    ma_list:计算ma,ema的移动天数
    vo:成交量的移动天数
    '''
    # 取出指定股票的数据
    data = all_data[all_data.code==code]
    # 按日期从小到大，这样才能直接使用pandas计算
    data = data.sort_values('dates',ascending=True)
    # 如果数据不足，就返回0
    if len(data)<=max(ma_list):
        return pd.DataFrame()
    # 计算前复权的ema
    for ma in ma_list:
        data['qema_' + str(ma)] = pd.ewma(data['qfq'], span=ma)
        data['qema_' + str(ma)] =data['qema_' + str(ma)].apply(lambda s:round(s,2))
    # 计算成交量ema
    data['vo_' + str(vo)] = pd.ewma(data['volume'], span=vo)
    data['vo_' + str(vo)] = data['vo_' + str(vo)].apply(lambda s:round(s,2))
    return data 

def coe(data,ma_list=[7,15,25,34,56,235],n=7):
    '''计算八线的coe值，使用最近7天的数据计算。
    使用的是 stock_ema 刚计算完ema后的df
    '''
    # 先排序,升序，最近的日期放在最后
    data2=data.sort_values('dates',ascending=True)
    # 分别计算多日指数移动平均的斜率
    all_coe=pd.Series()
    # 计算前复权的ema斜率
    for ma in ma_list:
        all_coe['qcoe_' + str(ma)]=sk.coe_n(data2['qema_' + str(ma)], n=n)
    # 返回一个series
    return all_coe

def line8_fqa(data,all_coe):
    '''回答一些问题，data是计算ema后的data，all_coe是斜率数据。
    返回一个series'''
    line_faq = pd.Series()
    # 计算如下问题
    # 1.7天线是否连续两天低于股价
    low7_2day=0  # 初始化为0
    p2,ema2 = data.iloc[-2][['qfq','qema_7']]
    p1,ema1 = data.iloc[-1][['qfq','qema_7']]
    if p2<ema2 and p1<ema1:
        low7_2day=1
    line_faq['low7_2day']=low7_2day
    # 2.股价是否大于持股线（15天线）
    up15=0  # 初始化为0
    p,ema15=data.iloc[-1][['qfq','qema_15']]
    if p>ema15:
        up15=1
    line_faq['up15']=up15
    # 3.股价是否大于生命线（25天线）
    up25=0  # 初始化为0
    p,ema25=data.iloc[-1][['qfq','qema_25']]
    if p>ema25:
        up25=1
    line_faq['up25']=up25
    # 4.生命线（25天线）是否向上
    # 这里的斜率值需要再考虑下
    ema25_up = 0
    ema25 = all_coe['qcoe_25']
    if ema25>0.1:  # 人为定0.05是否合适
        ema25_up=1
    if ema25<-0.1:
        ema25_up=-1
    line_faq['ema25_up']=ema25_up
    # 5.长牛线跟庄：长牛线（235天线）开始转头向上，股价>长牛线
    ema235_up = 0  # 初始化为0
    coe235 = all_coe['qcoe_235']
    if coe235>0.05:
        ema235_up=1
    if coe235<-0.05:
        ema235_up=-1
    line_faq['ema235_up']=ema235_up
    # 股价>长牛线
    up235 = 0  # 初始化为0
    p,ema235=data.iloc[-1][['qfq','qema_235']]
    if p>ema235:
        up235=1
    line_faq['up235']=up235
    # 返回结果的series
    return line_faq


    
def main(logger=''):
    # 导入日志
    logger = conf.get_log()
    logger.info('---------------------------------------')
    logger.info('开始计算ema,coe和八线理论')
    # 获取数据库连接
    conn = mydb.connect()
    #---------------------------------------------
    # 取出数据库内最大日期，格式是20150103
    sql = " select max(dates) from stock_qx "
    dates = pd.read_sql(sql,conn)
    dates = dates.ix[0,0]
    
    # 取出所有股票的指定时间长度区间500天的数据
    # 注意，如果当天该股没有数据，比如停牌，就没有必要计算了
    all_data = take_all_data(conn, dates, limit_row=-500)
    # 取出所有股票代码
    all_codes = list(set(all_data['code']))

    logger.info("完成获取 '当前日期'"+dates+" 和 '股票列表'")
    #---------------------------------------------
    # 开始计算ema,coe和八线理论
    # 循环计算每个股票的ma,ema,coe,faq
    t1 = time.clock()
    all_ema_coe_faq=[]
    for code in all_codes:
        data = stock_ema(code, all_data)  # 计算ema值
        if not len(data):  # 如果数据量不足，则继续下一个计算
            logger.info(code+" 不满足ema,coe数据量要求条件 ")            
            continue
        # 将每只股票的当天的ema数据保存，后面要入库
        data = data.sort_values('dates',ascending=True)
        # 计算coe斜率，使用近7天的数据拟合
        all_coe= coe(data,ma_list=[7,15,25,34,56,235])
        # 计算八线理论的一些指标
        line_faq = line8_fqa(data,all_coe)
        # 将ema,coe和FAQ组合在一起，并添加code和dates列
        ema_coe_faq = data.iloc[-1].append(all_coe)
        ema_coe_faq = ema_coe_faq.append(line_faq)
        # 集成到总的数据中
        all_ema_coe_faq.append(ema_coe_faq)
        logger.info(code+" 完成 ema,coe ")
    sp = "计算所有股票的数据的指标耗时："+str(round(time.clock()-t1,2))+" 秒"
    logger.info(sp)
    #---------------------------------------------
    # 获取列名，从而list转成数据框
    cols = ema_coe_faq.index
    calc_df = pd.DataFrame(all_ema_coe_faq,columns=cols)
    # 先在数据目录下备份计算结果
    calc_file1 = 'ema_coe计算结果_'+dates+'.csv'
    calc_file1 = os.path.join(calc_result_path,calc_file1)
    calc_df.to_csv(calc_file1,index=False)
    
    # 存放最新一份结果，方便在办公室直接读取
    calc_file2 = 'ema_coe计算结果.csv'
    calc_file2 = os.path.join(calc_result_path2,calc_file2)
    calc_df.to_csv(calc_file2,index=False)
    
    logger.info('今天的计算结果备份到'+calc_file1)
    #---------------------------------------------
    # 转成数据框，然后入库
    table = 'stock_ema'
    # 先删除今天的数据
    mydb.delete_today_data(conn,table,date=dates)
    # 入库
    mydb.df_into_db(conn,table,calc_df,types='insert')
    
    logger.info('今天的计算结果入库成功，程序将退出')
    
if __name__ == '__main__':
    main()
    

    
